package com.tanhua.dubbo.server.api;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.enums.CommentType;
import com.tanhua.dubbo.server.enums.IdType;
import com.tanhua.dubbo.server.pojo.*;
import com.tanhua.dubbo.server.service.IdService;
import com.tanhua.dubbo.server.service.TimeLineService;
import com.tanhua.dubbo.server.vo.PageInfo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import cn.hutool.core.collection.CollUtil;
import org.springframework.data.redis.core.RedisTemplate;


import java.util.ArrayList;
import java.util.List;

/**
 * @author LOVE
 */ /* 好友动态api实现 */
@Service(version = "1")
public class SelectFriendsApiImpl implements SelectFriends {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private IdService idService;
    @Autowired
    private TimeLineService timeLineService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 查询好友动态方法
     *
     * @param userID
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> selectFriends(Long userID, int page, int pageSize) {

        //朋友圈动态是根据好友动态发布时间距离显示的直接查发布表成本太高,通过先查询时间线表得到所有好友的动态发布时间和好友用户ID后再去发布表查询数据效率高很多
        //"quanzi_time_line_" + userID 动态表名,映射实体类@Document(collection = "quanzi_time_line_{userId}")
        List<TimeLine> timeLines = mongoTemplate.find(
                new Query()
                        .with(Sort.by(Sort.Order.desc("date")))
                        .limit(pageSize).skip((long) (page - 1) * pageSize),
                TimeLine.class,
                "quanzi_time_line_" + userID);
        if (timeLines.get(0) == null) {
            return null;
        }

        //获取集合中的值并封装到新的集合中
        List<Object> publishId = CollUtil.getFieldValues(timeLines, "publishId");

        //通过好友用户ID查询发布表好友的详细信息.
        List<Publish> publishes = mongoTemplate.find(
                new Query(
                        Criteria
                                .where("id")
                                .in(publishId))
                        .with(Sort.by(Sort.Order.desc("created")))
                , Publish.class);
        if (publishes.get(0) == null) {
            return null;
        }

        //数据封装
        return new PageInfo(0, page, pageSize, publishes);
    }

    /**
     * 发布动态方法
     *
     * @param publish 发布内容实体类
     * @return publishID 动态ID
     */
    @Override
    public String insertDynamic(Publish publish) {
        //对publish中的userID和发布动态的内容(文字)进行判断,为空返回空
        if (ObjectUtil.isAllEmpty(publish.getUserId(), publish.getText())) {
            return null;
        }

        try {
            //对publish的动态ID(主键ID),pid进行生成,记录发布时间
            publish.setId(ObjectId.get());
            publish.setPid(idService.createId(IdType.PUBLISH));
            //拿到当前时间
            publish.setCreated(System.currentTimeMillis());
            mongoTemplate.insert(publish);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            //将动态记录到相册表中(自己的动态记录表)
            Album album = new Album(new ObjectId(), publish.getId(), publish.getCreated());
            mongoTemplate.insert(album, "quanzi_album_" + publish.getUserId());

            //用异步的方式将动态的ID写到所有的好友时间线表中去
            timeLineService.insertTimeLine(publish.getUserId(), publish.getId());

        } catch (Exception e) {
            e.printStackTrace();
        }

        //toHexString方法将ObjectID id作为24字节十六进制字符串表示形式返回。
        return publish.getId().toHexString();
    }

    /**
     * 查询推荐动态方法
     *
     * @param userID
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> selectRecommend(Long userID, int page, int pageSize) {

        PageInfo pageInfo = new PageInfo<>(0, page, pageSize, null);

        // 从Redis中查询推荐结果数据,无数据返回空
        String key = "QUANZI_PUBLISH_RECOMMEND_" + userID;
        String data = this.redisTemplate.opsForValue().get(key);
        if (StrUtil.isEmpty(data)) {
            return pageInfo;
        }

        //将pid串切割成单独的pid
        List<String> pids = StrUtil.split(data, ',');

        //拿到起始数据和结束数据,PageUtil.transToStartEnd(page - 1, pageSize)等同于startEnd1
        //int[] startEnd1={page - 1 * pageSize,pageSize};
        int[] startEnd = PageUtil.transToStartEnd(page - 1, pageSize);
        //开始数据
        int startIndex = startEnd[0];
        //结束数据
        int endIndex = Math.min(startEnd[1], pids.size()); //结束
        //循环取值
        List<Long> pidLongList = new ArrayList<>();
        for (int i = startIndex; i < endIndex; i++) {
            //拿到十条推荐用户的ID
            pidLongList.add(Long.valueOf(pids.get(i)));
        }

        //判断是否为空
        if (CollUtil.isEmpty(pidLongList)) {
            //没有查询到数据
            return pageInfo;
        }

        //根据pid查询publish表的"pid"列并且以发布时间进行降序排列
        Query query = Query.query(Criteria.where("pid").in(pidLongList))
                .with(Sort.by(Sort.Order.desc("created")));
        //得到推荐用户的详细信息
        List<Publish> publishList = mongoTemplate.find(query, Publish.class);
        if (CollUtil.isEmpty(publishList)) {
            //没有查询到数据
            return pageInfo;
        }
        //拿到数据后进行封装并返回
        pageInfo.setRecords(publishList);
        return pageInfo;
    }

    /**
     * 根据动态的ID查询数据Publish表
     *
     * @param ID 动态id
     * @return
     */
    @Override
    public Publish selectID(String ID) {
        return mongoTemplate.findById(new ObjectId(ID), Publish.class);
    }

    /**
     * 根据动态的ID查询数据Comment表
     *
     * @param ID 动态id
     * @return
     */
    public Comment selectCommentID(String ID) {
        return mongoTemplate.findById(new ObjectId(ID), Comment.class);
    }

    /**
     * 根据动态的ID查询数据video表
     *
     * @param ID 动态id
     * @return
     */
    public Video selectVideoID(String ID) {
        return mongoTemplate.findById(new ObjectId(ID), Video.class);
    }


    /**
     * 根据用户ID和publishId进行点赞或喜欢方法
     *
     * @param userId
     * @param publishId
     * @return 返回是否点赞成功
     */
    @Override
    public Boolean likeLoveComment(Long userId, String publishId, CommentType commentType, String text) {
        Integer type = 1;
        //查询是否已经点赞或喜欢,是则返回true
        Boolean getIsTrue = getWhetherItIsTrue(publishId, userId, commentType);
        if (getIsTrue) {
            return true;
        }

        //还没点赞就先把这次点赞或喜欢的记录存到Comment表中(对所有的点赞或喜欢进行持久化,防止Redis中数据丢失)
        Boolean b = setComment(userId, publishId, commentType, text, type);
        //false代表保存失败!
        if (!b) {
            return false;
        }

        //修改Redis中的数据,可根据返回值进行数据回滚,未实现

        return setWhetherItIsTrue(publishId, userId, commentType, text, type);
    }

    /**
     * 对Redis中的操作进行修改功能方法
     *
     * @param publishId
     * @param userId
     * @param commentType
     * @param text
     * @return
     */
    private Boolean setWhetherItIsTrue(String publishId, Long userId, CommentType commentType, String text, Integer type) {
        //得到RedisKey
        String redisKey = "QUANZI_COMMENT_" + publishId;
        //获取commentType.值并判断.
        int cType = commentType.getType();
        String comType = commentType.toString();
        //点赞
        if (cType == 1) {
            try {
                //获取RedisKey的值(哈希结构)的key
                String redis_Key_value_key = "USER_LIKE_" + userId;
                //修改Redis数据
                return setRedis(type, redisKey, redis_Key_value_key, comType);
            } catch (Exception e) {
                //没添加成功可以在这里把MongoDB中的数据删除,达到数据回滚的效果
                e.printStackTrace();
            }
        }
        //评论
        else if (cType == 2) {
            return false;
        }
        //喜欢
        else {
            try {
                //获取RedisKey的值(哈希结构)的key
                String redis_Key_value_key = "USER_LOVE_" + userId;
                //修改Redis数据
                return setRedis(type, redisKey, redis_Key_value_key, comType);
            } catch (Exception e) {
                //没添加成功可以在这里把MongoDB中的数据删除,达到数据回滚的效果
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 添加或删除操作功能方法
     *
     * @param type
     * @param redisKey
     * @param redis_Key_value_key
     * @return
     */
    private Boolean setRedis(Integer type, String redisKey, String redis_Key_value_key, String comType) {
        try {
            if (type == 1) {
                //修改数据为已点赞或喜欢(1为点赞)
                redisTemplate.opsForHash().put(redisKey, redis_Key_value_key, "1");
                //修改点赞个数,increment()自增方法
                redisTemplate.opsForHash().increment(redisKey, comType, 1);
                return true;
            } else {
                //修改数据为未点赞或喜欢(1为点赞)
                redisTemplate.opsForHash().put(redisKey, redis_Key_value_key, "0");
                //修改点赞个数,increment()自增方法
                redisTemplate.opsForHash().increment(redisKey, comType, -1);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 对任何操作进行保存(MongoDB)功能方法
     *
     * @param userId
     * @param publishId
     * @param commentType
     * @param text
     * @param type        判断是添加还是删除 1为添加0为删除
     * @return
     */
    @Override
    public Boolean setComment(Long userId, String publishId, CommentType commentType, String text, Integer type) {
        try {
            if (type == 1) {
                Comment comment = new Comment();
                //类型
                comment.setCommentType(commentType.getType());
                //当前操作的主键ID
                comment.setId(new ObjectId());
                //操作的时间
                comment.setCreated(System.currentTimeMillis());
                //操作的用户的ID
                comment.setUserId(userId);
                //被操作的用户的publishId
                comment.setPublishId(new ObjectId(publishId));
                //操作(评论)的内容
                comment.setContent(text);
                //查询ID
                Publish publish = selectID(publishId);
                Comment comment1 = selectCommentID(publishId);
                //被操作的用户的Id
                comment.setPublishUserId(publish == null ? comment1 == null ? selectVideoID(publishId).getUserId() : comment1.getUserId() : publish.getUserId());

                mongoTemplate.insert(comment);
                return true;
            } else {
                mongoTemplate.remove(new Query(Criteria
                        .where("userId")
                        .is(userId)
                        .and("publishId").is(new ObjectId(publishId))
                        .and("commentType").is(commentType.getType())), Comment.class);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 取消点赞或喜欢方法
     *
     * @param userId
     * @param publishId
     * @param commentType
     * @return
     */
    @Override
    public Boolean disLikeComment(Long userId, String publishId, CommentType commentType, String text) {
        Integer type = 0;
        //查询是否已经点赞或喜欢,否则返回true
        Boolean getIsTrue = getWhetherItIsTrue(publishId, userId, commentType);
        if (!getIsTrue) {
            return true;
        }

        //是点赞或喜欢就在Comment表中对数据进行删除
        Boolean b = setComment(userId, publishId, commentType, null, type);
        //false代表保存失败!
        if (!b) {
            return false;
        }

        //修改Redis中的数据,可根据返回值进行数据回滚,未实现
        Boolean setIsTrue = setWhetherItIsTrue(publishId, userId, commentType, text, type);

        return setIsTrue;

    }

    /**
     * 查询点赞或喜欢或评论个数方法
     *
     * @param publishId
     * @param commentType
     * @return
     */
    @Override
    public Long queryLikeCount(String publishId, CommentType commentType) {
        //得到RedisKey
        String redisKey = "QUANZI_COMMENT_" + publishId;

        int type = commentType.getType();
        if (type == 1) {
            //获取RedisKey的值(哈希结构)的key
            String like = commentType.toString();
            //获取点赞数
            Object likes = redisTemplate.opsForHash().get(redisKey, like);
            if (likes != null) {

                return Long.valueOf(String.valueOf(likes));
            } else {
                return 0L;
            }
        }
        //评论
        else if (type == 2) {
            //获取RedisKey的值(哈希结构)的key
            String like = commentType.toString();
            //获取评论数
            Object likes = redisTemplate.opsForHash().get(redisKey, like);
            if (likes != null) {
                return Long.valueOf((String) likes);
            } else {
                return 0L;
            }
        }
        //喜欢
        else {
            //获取RedisKey的值(哈希结构)的key
            String like = commentType.toString();
            //获取喜欢数
            Object likes = redisTemplate.opsForHash().get(redisKey, like);
            if (likes != null) {
                return Long.valueOf(likes.toString());
            } else {
                return 0L;
            }
        }
    }


    /**
     * 判断是否点赞或喜欢方法
     *
     * @param publishId   动态或评论或喜欢的ID
     * @param userId      当前用户ID
     * @param commentType 是否是点赞或评论或喜欢
     * @return 是否点赞或喜欢
     */
    @Override
    public Boolean getWhetherItIsTrue(String publishId, Long userId, CommentType commentType) {
        //得到RedisKey
        String redisKey = "QUANZI_COMMENT_" + publishId;
        //获取commentType.值并判断.
        int type = commentType.getType();
        //点赞
        if (type == 1) {
            //获取RedisKey的值(哈希结构)的key
            String redis_Key_value_key = "USER_LIKE_" + userId;
            Object user_like = redisTemplate.opsForHash().get(redisKey, redis_Key_value_key);
            if (user_like != null) {
                //Convert.toStr()方法在值为null的情况下对值转换不会抛异常,而是还为null
                return "1".equals(Convert.toStr(user_like.toString()));
            } else {
                return false;
            }
        }
        //评论
        else if (commentType == CommentType.COMMENT) {
            //todo
            return false;
        }
        //喜欢
        else {
            //获取RedisKey的值(哈希结构)的key
            String redis_Key_value_key = "USER_LOVE_" + userId;
            Object user_like = redisTemplate.opsForHash().get(redisKey, redis_Key_value_key);
            if (user_like != null) {
                //Convert.toStr()方法在值为null的情况下对值转换不会抛异常,而是还为null
                return "1".equals(Convert.toStr(user_like.toString()));
            } else {
                return false;
            }
        }
    }

    /**
     * 查询评论
     *
     * @param publishId
     * @param page
     * @param pageSize
     * @param comment
     * @return
     */
    @Override
    public List<Comment> selectComments(String publishId, Integer page, Integer pageSize, CommentType comment) {
        return mongoTemplate.find(new Query(Criteria
                        .where("publishId")
                        .is(new ObjectId(publishId))
                        .and("commentType")
                        .is(comment.getType()))
                        .with(Sort.by(Sort.Order.desc("created")))
                        .limit(pageSize)
                        .skip((long) (page - 1) * pageSize)
                , Comment.class);
    }
}
